package net.cyndeline.temporary.gui

import com.escalatesoft.subcut.inject.BindingModule
import com.escalatesoft.subcut.inject.Injectable
import javafx.animation.KeyFrame
import javafx.application.Application
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyCodeCombination
import javafx.scene.input.KeyCombination
import javafx.scene.input.KeyEvent
import javafx.stage.Stage
import javafx.util.Duration
import net.cyndeline.GUI.GameObjectKey
import net.cyndeline.GUI.MapModifier
import net.cyndeline.GUI.PlayerEngineContainer
import net.cyndeline.GUI.PlayerGUIContainer
import net.cyndeline.GameMap.LevelMap
import net.cyndeline.Global
import net.cyndeline.components.world.TurnStorage
import net.cyndeline.components.world.TurnStorageComponent
import net.cyndeline.entitySystem.EntityManager
import net.cyndeline.entitySystem.EntityRef
import net.cyndeline.entitySystem.EventSystem
import net.cyndeline.events.gameplay.BeginTurn
import net.cyndeline.game.EventProcessor
import net.cyndeline.game.TurnHandler
import net.cyndeline.game.defaultImpl.ProcessCycle
import net.cyndeline.subcut.ProjectConfiguration
import net.cyndeline.temporary.TempDataHolder
import net.cyndeline.temporary.entityBuild.TempMapBuilder
import net.cyndeline.temporary.entityBuild.TempMapBuilder
import net.cyndeline.temporary.entityBuild.TempObjectBuilder
import javafx.event.ActionEvent
import net.cyndeline.GUI.PaintManager
import net.cyndeline.GUI.graphics.MapToPaint
import scala.actors.Actor
import scala.util.Random
import javafx.animation.TimelineBuilder
import javafx.animation.Animation

object TestMapLauncherMain {
  def main(args: Array[String]): Unit = {
    val l = new TestMapLauncher()
    Application.launch(classOf[TestMapLauncher], args: _*)
  }
}

/**
 * Launches the graphical part of a test map.
 */
class TestMapLauncher extends Application {
  val mapWidth = 12
  val mapHeight = 10
  
  override def start(primary: Stage) {
    implicit val bindingModule = ProjectConfiguration
    
    primary.setTitle("A map")
    val stuff = new DIContainerJavaFX()
    val mapModifier = stuff.run
    
    val scene = new Scene(mapModifier.map, 400, 400)
    stuff.setupKeyMovement(scene)
    primary.setScene(scene)
    primary.show()
    
    /* Start the painting. */
          val duration: Duration = Duration.millis(1000 / 60)
	      val handler = new EventHandler[ActionEvent]() {
        	override def handle(event: ActionEvent) {
        	  mapModifier.paint
	        }
	      }
          
	      val frame: KeyFrame = new KeyFrame(duration, handler)
	      
	      TimelineBuilder.create().cycleCount(Animation.INDEFINITE)
	      	.keyFrames(frame)
	      	.build()
	      	.play()
    
  }
  
  /* Private class since the main JavaFX application class can't handle
   * the implicit dependency injection module.
   */
  class DIContainerJavaFX(implicit val bindingModule: BindingModule) extends Injectable {
    
    def run: MapModifier = {
      val mapBuilder = new TempMapBuilder()
	    val mapStructure = mapBuilder.testmap1(mapWidth, mapHeight)
	    val mapModifier: MapModifier = inject[MapModifier]
	    mapModifier.create(264, 220, mapWidth, mapHeight)
	    
	    val objectBuilder = new TempObjectBuilder()
	    val player = objectBuilder.build(GameObjectKey.player).get
	    
	    val input = inject[PlayerEngineContainer]
	    input.player = player
	    
	    mapBuilder.addMapComponentData(player, mapStructure, 1, 1)
	    mapStructure.addObject(player)
	    mapModifier.addObjects(1, 1, List(player))
	    
	    for (m <- 0 until mapWidth; n <- 0 until mapHeight) {
	      val entities = mapStructure.objectsAt(m, n)
	      mapModifier.addObjects(m, n, entities)
	    }
	    
	    val setupESys = new TempDataHolder()
	    setupESys.run
	    
	    val sys = inject[EventSystem]
	    val looper = inject[EventProcessor]
	    looper.start()
	    looper ! ProcessCycle(sys)
	    
	    /* Add global entity */
	    val manager = inject[EntityManager]
	    val turnQueueComp = injectOptional[TurnStorageComponent] getOrElse new TurnStorage()
	    val global = manager.create(turnQueueComp)
	    manager.addKeyEntity(Global, global)
	    
	    /* Add player to turn queue */
	    val turnQueue = inject[TurnHandler]
	    turnQueue.add(player)
	    player.send(new BeginTurn())
	    
	    /* Start the painting. */
//	    val painterLoop = inject[PaintManager]
//	    painterLoop.start()
//	    painterLoop ! new MapToPaint(mapModifier)
	    
	    mapModifier
    }
    
    def setupKeyMovement(scene: Scene) {
    val keyEventHandler: EventHandler[KeyEvent] =
        new EventHandler[KeyEvent]() {
      		val input = inject[PlayerGUIContainer]
      		
            def handle(keyEvent: KeyEvent) {
              val left: KeyCombination = new KeyCodeCombination(KeyCode.LEFT);
              if (left.`match`(keyEvent)) {
                input.move(-1, 0)
              }
              
              val up: KeyCombination = new KeyCodeCombination(KeyCode.UP);
              if (up.`match`(keyEvent)) {
                input.move(0, -1)
              }
              
              val right: KeyCombination = new KeyCodeCombination(KeyCode.RIGHT);
              if (right.`match`(keyEvent)) {
                input.move(1, 0)
              }
              
              val down: KeyCombination = new KeyCodeCombination(KeyCode.DOWN);
              if (down.`match`(keyEvent)) {
                input.move(0, 1)
              }
            }
        }
    
      scene.setOnKeyPressed(keyEventHandler)
  	}
  }
}